#ifndef xpack_lang_cxx_make_base64
#define xpack_lang_cxx_make_base64
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::lang_cxx::inc
#include"define/base_type.hpp"
#include"interface/unified_seq.hpp"
#include"lang/cxx/private/lut.base64x.hpp"
#include"lang/cxx.hpp"
#include"macro/xforward.hpp"
#include"macro/xunlikely.hpp"
#include"meta/remove_ref.hpp"
#include"utils/bytes_array_wrapper.hpp"
#pragma pop_macro("xuser")

namespace mixc::lang_cxx{
    template<class item_t>
    inline void make_base64_core(asciis lut, auto const & seq, auto const & alloc){
        enum { max_buffer = 4 };
        item_t buffer[max_buffer];
        auto width          = seq->length * 4;
        auto size           = width / 3;
        auto total_length   = ~0x3 & (size + 3);
        auto stream         = alloc(total_length);
        auto temp           = u32(0);
        auto len            = uxx(seq->length);
        auto i              = uxx(0);
        auto j              = uxx(0);
        auto k              = uxx(0);
        auto l              = uxx(0);

        xunlikely(stream == nullptr){
            return;
        }

        while(i + 3 <= len){
            temp            = temp << 8 | u08(seq[i++]);
            temp            = temp << 8 | u08(seq[i++]);
            temp            = temp << 8 | u08(seq[i++]);
            buffer[0]       = lut[(temp >> 18) & 0x3f];
            buffer[1]       = lut[(temp >> 12) & 0x3f];
            buffer[2]       = lut[(temp >> 06) & 0x3f];
            buffer[3]       = lut[(temp >> 00) & 0x3f];
            stream.output(buffer, max_buffer);
        }

        if (i != len){
            for(temp = 0, k = i; i < len; ){
                temp        = temp << 8 | u08(seq[i++]);
            }

            for(temp <<= (4 - (i - k)) * 8, l = j + i - k + 1; j < l; ){
                buffer[j++] = lut[temp >> (32 - 6)];
                temp      <<= 6;
            }

            if (lut != make_base64uri) while(j & 0x3){
                buffer[j++] = '=';
            }

            stream.output(buffer, j);
        }
    }

    template<class item_t>
    template<inc::can_unified_seqlize seq_t, class xalloc_t, class ret_t, class>
    inline cxx<item_t>::cxx(base64_t const & mode, seq_t && seq, xalloc_t const & alloc) : cxx<item_t>(){
        using remove_ref = inc::unified_seq<inc::remove_ref<seq_t>>;
        inc::bytes_array_wrapper<remove_ref> source = xforward(seq);
        auto allocx         = the_t::helper::stream_helper(alloc, xmodify(m_ptr), xmodify(m_length));
        make_base64_core<item_t>(mode, source, allocx);
    }
}

#endif

xexport(mixc::lang_cxx::make_base64)
xexport(mixc::lang_cxx::make_base64uri)
